Laravel 是目前 PHP 很流行的框架,今天以看到 Laravel 的預設歡迎頁為目標,建置 Laravel image。
首先一開始,要先把 Laravel 主程式先準備好。參考 Installing Laravel 文件,安裝 PHP 7.3 與 Composer,然後執行下面指令即可把 Laravel 程式安裝至 blog 目錄。
composer create-project --prefer-dist laravel/laravel blog
接著進 blog 目錄啟動 server:
cd blog
php artisan serve
完成後,看到 Laravel 的預設歡迎頁,程式碼就準備完成了。
先定義 Docker 要下什麼指令,會達到跟官方執行 php artisan serve 一樣的結果:
docker run --rm -it -p 8000:8000 laravel
轉換成 docker-compose.yml 如下:
version: "3.8"
services:
laravel:
image: laravel
stdin_open: true
tty: true
其中 -p 8000:8000 是配合預設開 8000 port;image 取名為 laravel。
撰寫 Dockerfile 的過程中,會不斷重複啟動與移除 container 測試,筆者會用下面 Makefile 來簡化指令,示範和說明也比較清楚一點:
#!/usr/bin/make -f
IMAGE := laravel
VERSION := latest
.PHONY: all build rebuild shell run
# ------------------------------------------------------------------------------
all: build
# 建置 image
build:
docker build -t=$(IMAGE):$(VERSION) .
# 不使用 cache 建置 image
rebuild:
docker build -t=$(IMAGE):$(VERSION) --no-cache .
# 執行並使用 shell 進入 container
shell:
docker run --rm -it -p 8000:8000 $(IMAGE):$(VERSION) bash
# 執行 container
run:
docker run --rm -it -p 8000:8000 $(IMAGE):$(VERSION)
筆者認為寫 Dockerfile 跟 TDD 一樣有三循環如下:
docker build 並驗證是否正確撰寫 Dockerfile 的第一手,是先寫一個可以驗證成功的 Dockerfile,後續就可以走上面的三循環。
昨天範例有提到 FROM 也是一個步驟。只要 image 在 DockerHub 能下載得到,Dockerfile 就能 build 成功,我們可以寫一個只有 FROM 的 Dockerfile。Laravel 官網的 Server Requirements 要求 PHP >= 7.3,因此我們使用 php:7.3 image:
FROM php:7.3
接著執行 make build 與 make shell 試看看:
make build
make shell
建置完成,並在 commit 上 tag laravel,同時進去 shell 確認 PHP 版本正確。
註:目前
laraveltag 與php:7.3tag 在同個 commit 上。
預設的路徑是根目錄 /,是個一不小心就會刪錯檔案的位置,可以換到一個比較安全的目錄,比方說 /source:
WORKDIR /source
WORKDIR 可以設定預設工作目錄。它同時是 docker build 過程與 docker run 的工作目錄,跟 -w 選項的意義相同。接著把 Laravel 原始碼複製進 container 裡,這裡使用 COPY 指令:
COPY . .
COPY 是把本機的檔案複製到 container 裡,使用方法為 COPY [hostPath] [containerPath]
要注意這裡有個雷,單一檔案複製沒有問題,但目錄複製就得小心。它的行為跟 Linux 常見的 cp 不大一樣。
cp -r somedir /some/path
以 cp 指令來說,上面指令執行完會多一個目錄 /some/path/somedir。
COPY somedir /container/path
COPY somedir/* /container/path
以 COPY 指令來說,上面兩個指令是等價的。原本預期會多一個 /container/path/somedir 目錄,實際上是 somedir 目錄裡所有東西全複製到 /container/path 下。
解決方法是改成下面這個指令:
COPY somedir /container/path/somedir
Docker Compose 裡有提到一個設定是 command,它定義了 container 啟動預設會執行的指令。Dockerfile 也有一樣用法的指令--CMD。而啟動 server 指令一開始 Laravel 建好的時候已經知道了,php artisan serve。
套用在 CMD 指令上的用法會有兩種如下:
# exec 模式,官方推薦
CMD ["php", "artisan", "serve"]
# shell 模式
CMD php artisan serve
未來會再解釋這兩個模式的差異,先用官方推薦來試試。
實際執行的時候會發現不能正常連到 server?原因很簡單,在說明 Port forwarding 的時候,曾用了下面這張圖:

當時提到每個 container 有屬於自己的 port,因為每個 container 都是獨立的個體,包括 host 也是一個獨立的個體。
再回頭看 Laravel 啟動 server 的資訊,它綁定了 127.0.0.1:8000 在 container 上。
Starting Laravel development server: http://127.0.0.1:8000
不能連線的原因其實非常單純,host 與 container 要視為兩台不一樣的機器,因為 container 僅綁定本機--也就是只有進 container 使用 curl http://127.0.0.1:8000 可以連線,而 host 連 container 會被視為外部連線,因此會連線失敗。
解決方法很單純,把綁定 IP 調整即可,下例是以 0.0.0.0 全部開放為例:
CMD ["php", "artisan", "serve", "--host", "0.0.0.0"]
建置服務成功!測試也完成了,恭喜大家成功用 Dockerfile 建置 Laravel image 成功!
今天 Dockerfile 最後長相如下:
FROM php:7.3
WORKDIR /source
COPY . .
CMD ["php", "artisan", "serve", "--host", "0.0.0.0"]
目前這個內容有很多缺陷,接下來會開始做 Dockerfile 最佳化,會一步步讓讀者知道更多 image 與 container 相關的技巧。
請問"設定路徑與原始碼",是要把"WORKDIR /source"和其他指令寫入原本的Dockerfile檔案裏? 用不用把執行中的make shell 指令停止? 最後是怎樣去執行dockerfile 裏的指令,應該用什麼command去執行?
當執行完成後是否會有以下的文字出現?
PS D:\docker\phpsource> make shell
docker run --rm -it -p 8000:8000 laravel:latest bash
root@4c90c339c0bd:/D:dockerphpsourcesource#
dockerfile內容:
FROM php:7.3
Thank you
make shell 當初設計的用意是,把寫好的 image 拿來用 bash / sh 跑看看。
然後就能在裡面執行 php -m 或其他相關除錯的指令。
它並不是用來執行 Dockerfile 的 CMD 用的。
如果我輸入了curl http://127.0.0.1:8000 ,都不能夠連線。請問是否我做錯了某一步驟?
原來我是沒有將Dockerfile, makefile, docker-compose.yml檔案放在laravel blog資料夾裏執行。所以,就出現錯誤和不能夠連線問題。
下面應該是成功執行後畫面
針對 Makefile 的部分
我也寫了一篇分享文
請 Miles 大大指教
https://blog.goodjack.tw/2023/01/use-makefile-to-manage-workflows-for-web-projects.html